home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
130 MIDI Tool Box
/
130 MIDI Tool Box.iso
/
ptmid
/
ptmidinp.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-01-08
|
12KB
|
397 lines
/*
* ptmidinp.c: MIDI input module for for ptmid. Reads a MIDI file and
* creates a structure representing it.
*
* Author: Andrew Scott (c)opyright 1994
*
* Date: 17/11/1993 ver 0.0
* 8/1/1994 ver 0.1
*/
#include <stdio.h>
#include <stdlib.h>
#include "ptmid.h"
#define ODD(x) (x & 1)
#define MYROUTINE
typedef unsigned long VLQ; /** VLQ is a variable length quantity **/
int rgbPatch[16], wDivision;
unsigned wQuant;
NRL *rgpnrl[128];
Tune *ptuneMain, *ptuneCurr;
/*
* Init: Yes.. you guessed it. Initializes variables and stuff.
*/
static void Init()
{
int i;
for (i = 16; i--; ) /** Clear current instrument array **/
rgbPatch[i] = 0;
for (i = 128; i--; ) /** Clear hanging-note array **/
rgpnrl[i] = NULL;
}
/*
* ValidquantSz: Takes a lower-case string and checks to see if it is a
* legal quantize fraction. If not, zero is returned, else value of
* string is returned (+1 if a triplet case).
*/
int ValidquantSz(Sz szQuant)
{
int bFrac;
Sz szEnd;
if ((bFrac = strtol(szQuant, &szEnd, 10))) /** If a number **/
if ('t' == *szEnd) /** possibly followed by a 't', then valid **/
bFrac++;
return bFrac;
}
/*
* VlqFromPfile: Reads bytes from given file until a variable length
* quantity is decoded. Returns that vlq.
*/
VLQ VlqFromPfile(FILE *pfile)
{
int b;
VLQ vlqRead;
#ifdef MYROUTINE
vlqRead = 0;
while ((b = getc(pfile)) & 128 && EOF != b)
vlqRead = (vlqRead << 7) + (b & 127);
return (vlqRead << 7) + b;
#else
if ((vlqRead = getc(pfile)) & 128) {
vlqRead &= 127;
do
vlqRead = (vlqRead << 7) + ((b = getc(pfile)) & 127);
while (b & 128 && EOF != b);
}
return vlqRead;
#endif
}
/*
* Addnote: Given pitch, instrument, and volume will add that note to the
* array of playing notes.
*/
void Addnote(int pitch, int inst, int vol)
{
NRL *pnrlT;
if (0 > pitch || 127 < pitch)
return;
pnrlT = (NRL *) malloc(sizeof(NRL)); /** Allocate space **/
pnrlT->pnrl = rgpnrl[pitch];
rgpnrl[pitch] = pnrlT; /** Attach to front of list in hanging-note array **/
pnrlT->inst = inst;
pnrlT->vol = vol;
pnrlT->ptuneNow = ptuneCurr;
}
/*
* PeiRequestPtune: Returns a pointer to a new event structure at the
* position in the tune specified.
*/
EI *PeiRequestPtune(Tune *ptune)
{
EI *pei;
pei = (EI *) malloc(sizeof(EI)); /** Allocate space for event **/
pei->peiNext = ptune->pei;
ptune->pei = pei; /** Attach to front of event list at tune position **/
return pei;
}
/*
* PeiLocatePtune: Given a duration and a position in the tune, will search
* through for chord events at that position with matching duration.
*/
EI *PeiLocatePtune(Tune *ptune, unsigned long durat)
{
EI *pei;
for (pei = ptune->pei; NULL != pei && !(pei->cni && pei->effect == durat); )
pei = pei->peiNext;
return pei;
}
/*
* Endnote: Given a pitch and instrument, will remove a note from array
* of playing notes and put it in a chord in the tune.
*/
void Endnote(int pitch, int inst)
{
NRL *pnrlT, *pnrlOld;
unsigned long durat;
EI *peiT;
NI *pniT;
if (0 > pitch || 127 < pitch)
return;
for (pnrlT = rgpnrl[pitch]; NULL != pnrlT && pnrlT->inst != inst; ) {
pnrlOld = pnrlT;
pnrlT = pnrlT->pnrl;
} /** Find instrument in hanging-note array **/
if (NULL == pnrlT)
return;
if ((durat = ptuneCurr->count - pnrlT->ptuneNow->count) == 0)
durat = wQuant; /** Calculate its (quantized) duration **/
if ((peiT = PeiLocatePtune(pnrlT->ptuneNow, durat)) == NULL) {
peiT = PeiRequestPtune(pnrlT->ptuneNow); /** If must start a new chord **/
peiT->effect = durat;
peiT->cni = 1;
pniT = (NI *) malloc(sizeof(NI)); /** start new note list **/
peiT->pni = pniT;
} else { /** Else **/
peiT->pni = (NI *) realloc(peiT->pni, sizeof(NI) * ++(peiT->cni));
pniT = peiT->pni + peiT->cni - 1; /** add to old note list **/
}
pniT->inst = inst; /** Update note info **/
pniT->pitch = pitch;
pniT->vol = pnrlT->vol;
if (rgpnrl[pitch] == pnrlT) /** Remove note from hanging-note array **/
rgpnrl[pitch] = pnrlT->pnrl;
else
pnrlOld->pnrl = pnrlT->pnrl;
free(pnrlT);
}
/*
* VlqInterpPpfile: Reads a file pointer and gathers all notes at this
* instant, storing them on the note stack. Returns ticks until next
* collection of notes. A running status of current channel is maintained.
*/
VLQ VlqInterpPpfile(FILE **ppfile, unsigned *pbStat)
{
unsigned bEvent;
VLQ vlqT = 0;
while (0 == vlqT) { /** While doing simultaneous events.. **/
bEvent = (unsigned) getc(*ppfile); /** Get first data byte **/
if (0x80 <= bEvent && 0xEF >= bEvent) { /** If a command **/
*pbStat = bEvent; /** update running-status byte **/
bEvent = (unsigned) getc(*ppfile); /** and get next data byte **/
}
if (0xF0 == bEvent || 0xF7 == bEvent) /** If a sys-exclusive message **/
fseek(*ppfile, VlqFromPfile(*ppfile), SEEK_CUR); /** skip it **/
else if (0xFF == bEvent) { /** Else if a meta event **/
bEvent = (unsigned) getc(*ppfile); /** get type of event **/
vlqT = VlqFromPfile(*ppfile); /** and length **/
if (0x2F == bEvent) { /*** If termination event ***/
fclose(*ppfile); /*** close handle ***/
*ppfile = NULL;
vlqT = 0;
break; /*** and terminate ***/
}
if (0x51 == bEvent) { /*** Else if tempo event ***/
unsigned long t;
EI *pei;
t = (unsigned long) getc(*ppfile) << 16; /*** get value ***/
t += (unsigned long) getc(*ppfile) << 8;
t += (unsigned long) getc(*ppfile);
pei = PeiRequestPtune(ptuneCurr);
pei->effect = 60000000L / wQuant * wDivision / t; /*** and convert ***/
pei->cni = 0;
} else
fseek(*ppfile, vlqT, SEEK_CUR); /*** Else skip event ***/
} else
switch (*pbStat & 0xF0) { /** Else must be a midi event.. **/
case 0x80:
case 0x90: { /** Note on/off **/
unsigned bVol, bChan;
bVol = getc(*ppfile);
bChan = *pbStat & 0x0F;
if (0 < bVol && 0x90 <= *pbStat)
if (bChan != bDrumch)
Addnote(bEvent, rgbPatch[bChan], bVol);
else
Addnote(0, -1 - bEvent, bVol);
else
if (bChan != bDrumch)
Endnote(bEvent, rgbPatch[bChan]);
else
Endnote(0, -1 - bEvent);
break;
}
case 0xA0: /** Polyphonic Key Pressure **/
case 0xB0: /** Controller change **/
case 0xE0: /** Pitch Wheel change **/
getc(*ppfile);
break;
case 0xC0: /** Program change **/
rgbPatch[*pbStat - 0xC0] = bEvent;
break;
case 0xD0: /** Channel Pressure **/
break;
}
vlqT = VlqFromPfile(*ppfile);
}
return vlqT;
}
/*
* Freearray: Performs a free on all structures left in rgpnrl array.
* Assume these notes are rebels.
*/
void Freearray()
{
int i = 128;
NRL *pnrlT, *pnrlT2;
while (i--) /** Go through hanging-note array **/
for (pnrlT = rgpnrl[i]; NULL != pnrlT; ) { /** freeing each list **/
pnrlT2 = pnrlT->pnrl;
free(pnrlT);
pnrlT = pnrlT2;
}
}
/*
* PtuneLoadPfile: Given the filename of a MIDI file, parse it and return
* a pointer to a collection of chords (a Tune structure). If MIDI file
* cannot be processed, NULL is returned.
*/
Tune *PtuneLoadFn(Sz FnMIDI)
{
FILE *pfile, **ppfile;
unsigned long cb, *pvlqWait, vlqMin = -1, vlqT, wCount, wNcount;
unsigned ippfile, ippfileMax, *pbStatus, wQuant2;
int cppfile;
Init();
pfile = fopen(FnMIDI, "rb");
if ('M' != getc(pfile) || 'T' != getc(pfile) || 'h' != getc(pfile) ||
'd' != getc(pfile) || 0 != getc(pfile) || 0 != getc(pfile) ||
0 != getc(pfile) || 6 != getc(pfile) || 0 != getc(pfile) ||
1 < (unsigned) getc(pfile)) {
fclose(pfile);
return NULL; /** Only process type 0 or type 1 general MIDI files **/
}
ippfileMax = (unsigned) getc(pfile) << 8;
ippfileMax += (unsigned) getc(pfile); /** Get # tracks **/
wDivision = (unsigned) getc(pfile) << 8;
wDivision += (unsigned) getc(pfile); /** Get ticks for a beat **/
if (fseek(pfile, 23, SEEK_SET)) {
fclose(pfile);
return NULL; /** Error if MIDI file is smaller than 23 bytes **/
}
if (fNocopy && getc(pfile) == 0xFF && getc(pfile) == 0x02) {
fclose(pfile);
if (!fQuiet)
printf("** NOCOPY option set and copyright notice found in file **\n");
return NULL; /** Error if Nocopy and copyright notice exists **/
}
if (32767 < wDivision) {
if (!fQuiet)
printf("** Slack programmer error -- SMPTE frames not supported **\n");
fclose(pfile);
return NULL;
}
if (ODD(wQuantval)) /** Calculate quantize ticks from quantize fraction **/
wQuant = wDivision * 8 / (3 * (wQuantval - 1));
else
wQuant = wDivision * 4 / wQuantval;
wQuant2 = wQuant/2;
if (!fQuiet) {
printf("Ticks to quantize: %u\n", wQuant);
printf("Number of tracks: %d\n", ippfileMax);
}
ppfile = (FILE **) malloc(sizeof(FILE *) * ippfileMax);
pvlqWait = (VLQ *) malloc(sizeof(VLQ) * ippfileMax);
pbStatus = (unsigned *) malloc(sizeof(unsigned) * ippfileMax);
fseek(pfile, 18, SEEK_SET);
/** Put file pointers at the start of each track in file **/
for (ippfile = 0; !feof(pfile) && ippfile < ippfileMax; ippfile++) {
cb = (unsigned long) getc(pfile) << 24;
cb += (unsigned long) getc(pfile) << 16;
cb += (unsigned long) getc(pfile) << 8;
cb += (unsigned long) getc(pfile);
if ((ppfile[ippfile] = fopen(FnMIDI, "rb")) == NULL) {
fprintf(stderr, "ptmid: No more files can be opened\n");
exit(1);
}
fseek(ppfile[ippfile], ftell(pfile), SEEK_SET);
if ((pvlqWait[ippfile] = VlqFromPfile(ppfile[ippfile])) < vlqMin)
vlqMin = pvlqWait[ippfile]; /** Find minimum time to first event **/
pbStatus[ippfile] = 0x90;
fseek(pfile, cb + 4, SEEK_CUR);
}
fclose(pfile);
if (ippfile != ippfileMax) {
printf("** MIDI file ends prematurely **\n");
free(ppfile);
free(pvlqWait);
free(pbStatus);
return NULL;
}
ptuneMain = ptuneCurr = (Tune *) malloc(sizeof(Tune));
wCount = vlqMin; /** Start from first event **/
wNcount = (wCount + wQuant2) % wQuant;
ptuneMain->count = wCount + wQuant2 - wNcount;
wNcount = wQuant - wNcount;
ptuneMain->ptune = NULL;
ptuneMain->pei = NULL;
cppfile = 1;
while (0 < cppfile) { /** While still tracks in file to process **/
cppfile = 0;
vlqT = -1;
for (ippfile = 0; ippfile < ippfileMax; ippfile++) /** With each track.. **/
if (NULL != ppfile[ippfile]) { /** If not finished **/
cppfile++;
/**
** Must keep all tracks in sync, if events occurring on this track
** at this instant, then interpret them. Also note when next event
** will occur (ie. minimum ticks to next event)
**/
if ((pvlqWait[ippfile] -= vlqMin) == 0)
pvlqWait[ippfile] = VlqInterpPpfile(&ppfile[ippfile], pbStatus +
ippfile);
if (pvlqWait[ippfile] < vlqT)
vlqT = pvlqWait[ippfile];
}
vlqMin = vlqT;
wCount += vlqMin;
if (wNcount <= vlqMin) { /** If need to advance to new quanta **/
if ((ptuneCurr->ptune = (Tune *) malloc(sizeof(Tune))) == NULL) {
fprintf(stderr, "ptmid: Cannot allocate any more memory\n");
exit(1);
} /** allocate **/
ptuneCurr = ptuneCurr->ptune; /** and initialize **/
wNcount = (wCount + wQuant2) % wQuant;
ptuneCurr->count = wCount + wQuant2 - wNcount;
wNcount = wQuant - wNcount;
ptuneCurr->ptune = NULL;
ptuneCurr->pei = NULL;
} else
wNcount -= vlqMin; /** Else decrememnt "new quanta" count **/
}
Freearray();
free(pvlqWait);
free(pbStatus);
free(ppfile);
return ptuneMain;
}